require "lingohub/client"

module Lingohub::Command
  class Auth < Base
    attr_accessor :credentials

    def client
      @client ||= init_lingohub
    end

    def init_lingohub
      client = Lingohub::Client.new(:username => user, :auth_token => auth_token, :host => host)
#      client.on_warning { |msg| self.display("\n#{msg}\n\n") }
      client
    end

    def host
      ENV['LINGOHUB_HOST'] || 'https://api.lingohub.com'
    end

    # just a stub; will raise if not authenticated
    def check
      client.projects.all
    end

    def reauthorize
      @credentials = ask_for_and_save_credentials
    end

    def user # :nodoc:
      get_credentials
      @credentials[0]
    end

    def auth_token # :nodoc:
      get_credentials
      @credentials[1]
    end

    def credentials_file
      "#{home_directory}/.lingohub/credentials"
    end

    def get_credentials # :nodoc:
      return if @credentials
      unless @credentials = read_credentials
        ask_for_and_save_credentials
      end
      @credentials
    end

    def read_credentials
      File.exists?(credentials_file) and File.read(credentials_file).split("\n")
    end

    def echo_off
      system "stty -echo"
    end

    def echo_on
      system "stty echo"
    end

    def ask_for_credentials
      puts "Enter your Lingohub credentials."

      print "Email: "
      user = ask


      print "Password  (please leave blank if you want to use your API token): "
      password = running_on_windows? ? ask_for_password_on_windows : ask_for_password

      if password.empty?
        print "API key: "
        api_key = ask
      else
        api_key = retrieve_api_key(password, user)
      end

      [user, api_key]
    end

    def retrieve_api_key(password, user)
      Lingohub::Client.auth(:username => user, :password => password, :host => host)['api_key']
    end

    def ask_for_password_on_windows
      require "Win32API"
      char = nil
      password = ''

      while char = Win32API.new("crtdll", "_getch", [], "L").Call do
        break if char == 10 || char == 13 # received carriage return or newline
        if char == 127 || char == 8 # backspace and delete
          password.slice!(-1, 1)
        else
          # windows might throw a -1 at us so make sure to handle RangeError
          (password << char.chr) rescue RangeError
        end
      end
      puts
      return password
    end

    def ask_for_password
      echo_off
      password = ask
      puts
      echo_on
      return password
    end

    def ask_for_and_save_credentials
      begin
        @credentials = ask_for_credentials
        write_credentials
        check
      rescue ::RestClient::Unauthorized, ::RestClient::ResourceNotFound => e
        puts "EXCEPTION #{e}"
        delete_credentials
        @client = nil
        @credentials = nil
        display "Authentication failed."
        retry if retry_login?
        exit 1
      rescue Exception => e
        delete_credentials
        raise e
      end
    end

    def retry_login?
      @login_attempts ||= 0
      @login_attempts += 1
      @login_attempts < 3
    end

    def write_credentials
      FileUtils.mkdir_p(File.dirname(credentials_file))
      f = File.open(credentials_file, 'w')
      f.chmod(0600)
      f.puts self.credentials
      f.close
      set_credentials_permissions
    end

    def set_credentials_permissions
      FileUtils.chmod 0700, File.dirname(credentials_file)
      FileUtils.chmod 0600, credentials_file
    end

    def delete_credentials
#      FileUtils.rm_f(credentials_file)
    end
  end
end
